package com.qdlc.p2p.biz.processor.task;

import java.text.MessageFormat;

import javax.annotation.Resource;

import com.qdlc.p2p.common.constant.TradeCode;
import org.apache.log4j.Logger;

import com.qdlc.p2p.biz.processor.event.CollectionTradeNotifyValueEvent;
import com.qdlc.p2p.biz.service.BorrowRepaymentService;
import com.qdlc.p2p.biz.service.BorrowService;
import com.qdlc.p2p.biz.service.TppService;
import com.qdlc.p2p.biz.service.TppTradeService;
import com.qdlc.p2p.common.constant.SinaConstant;
import com.qdlc.p2p.common.core.queue.TaskProcessorException;
import com.qdlc.p2p.common.core.queue.ValueEvent;
import com.qdlc.p2p.common.util.DateUtil;
import com.qdlc.p2p.dal.dto.Tpp;
import com.qdlc.p2p.dal.dto.TppTradeLog;
import com.qdlc.p2p.dal.model.TppModel;

/**
 * 代收/代付的逻辑抽象定义：
 * 	1. 代收： 平台发起代收请求(交易类型需要设置成{@code TradeCode.C1001}或者{@code TradeCode.C1002})，代收请求前平台会保存TppTradeLog实体记录，如果请求成功平台将会保存Tpp实体记录。（投标和自动投标不保存Tpp记录）
 * 	2. 代收通知： 在处理完代收请求后，新浪会发送代收通知。
 * 		(a) 根据返回的订单号找到TppTradeLog和Tpp(如果有保存记录)
 *      (b) 判断TppTradeLog记录状态，如果已经处理过则返回，否则处理订单
 *      (c) 判断交易状态TRADE_FINISHED、PAY_FINISHED、TRADE_FAILED(参考{@code TradeStatus})。
 *      	子类通过实现方法来实现具体的业务功能
 				supprotTradeFinished(String serviceType)
 *              doPay(TppTradeLog tppTradeLog)
 *              doFail(TppTradeLog tppTradeLog, Tpp tpp, CollectionTradeNotifyValueEvent event)
 *  3. 代付： 处理代收通知后，需要发起代付请求(交易类型需要设置成{@code TradeCode.C2001}或者{@code TradeCode.C2002})，代付请求在doPay(TppTradeLog tppTradeLog)中实现。
 *          代付请求和代收请求逻辑类似，在平台中也需要保存TppTradeLog和Tpp交易订单号(投标和自动投标不需要保存Tpp)
 *  4. 代付通知： 代付通知的处理和代收通知逻辑一样，只是代付通知时，不需要doPay(TppTradeLog tppTradeLog)。
 *
 * @see AbstractCollectionTradeNotifyBaseTaskProcessor
 * @see CollectionTradeNotifyRedEnvelopeTaskProcessor
 * @see CollectionTradeNotifyVirtualTaskProcessor
 * @see CollectionTradeNotifyC1002TaskProcessor
 * @see AbstractCollectionTradeNotifyTendeTaskProcessor
 * @see CollectionTradeNotifyAutoTenderTaskProcessor
 * @see CollectionTradeNotifyTenderTaskProcessor
 *
 * @see TppSinaCollectionTradeNotifyAction
 *
 * @author linya 2016-01-13
 *
 */
public abstract class AbstractCollectionTradeNotifyTaskProcessor extends AbstractTaskProcessor {

	private static final Logger logger = Logger.getLogger(AbstractCollectionTradeNotifyTaskProcessor.class);

	@Resource
	TppTradeService tppTradeService;

	@Resource
	TppService tppService;

	@Resource
	BorrowRepaymentService borrowRepaymentService;

	@Resource
	BorrowService borrowService;

	@Override
	public boolean supports(ValueEvent event) {
		return event instanceof CollectionTradeNotifyValueEvent;
	}

	/**
	 * 平台获取到代收/代付通知后调用业务处理逻辑
	 * @param event
	 * @throws TaskProcessorException
	 */
	@Override
	protected void process0(ValueEvent event) throws TaskProcessorException {

		CollectionTradeNotifyValueEvent notifyEvent = (CollectionTradeNotifyValueEvent)event;
		String outTradeNo = notifyEvent.getOutOrderNo();
		String depositStatus = notifyEvent.getDepositStatus();

		// 新浪调用, TppTradeLog都会生成（相同的订单号可能会产生并发问题）
		TppTradeLog tppTradeLog = tppTradeService.find(outTradeNo);

		// 自动投标和投标不生成tpp
		Tpp tpp = tppService.getTppByOrdId(outTradeNo);
		if ((tppTradeLog == null) && (tpp != null)) {
			tppTradeLog = tppTradeService.find(tpp.getSubOrdId());
		}

		// 异步回调 订单状态为成功情况
		if (tppTradeLog == null) {
			logger.info(MessageFormat.format("++++ 订单号{0}不存在!", outTradeNo));
			return;
		}

		//获取代收/代付类型，用于日志输出
		String loggerPrefix = getLoggerPrefix(tppTradeLog.getTradeType());

		// 判断是否已经成功过
		String sinaStatus = tppTradeLog.getSinaStatus();

		// 订单号已经被处理过了
		if(sinaStatus != null && sinaStatus.contains(depositStatus)){
			logger.info(MessageFormat.format("++++ {0}订单号{1},状态{2},已经被执行过!", loggerPrefix, outTradeNo, depositStatus));
			return;
		}

		//更新回调后状态
		if(sinaStatus == null){
			tppTradeLog.setSinaStatus(depositStatus);
		}else{
			tppTradeLog.setSinaStatus(sinaStatus + " " + depositStatus);
		}

		logger.info(MessageFormat.format("++++ 开始处理{0}订单号{1},交易类型{2},服务类型{3},状态{4}!", loggerPrefix, outTradeNo, tppTradeLog.getTradeType(), tppTradeLog.getServiceType(), depositStatus));

		// 异步回调 订单状态为已结束情况
		if (SinaConstant.TRADE_STATUS_FINISHED.equals(depositStatus)) {
			dosTradeFinished(tppTradeLog, tpp, notifyEvent);
		} else if (SinaConstant.TRADE_STATUS_PAY_FINISHED.equals(depositStatus)) {
			doPayFinished(tppTradeLog, notifyEvent);
		} else if (SinaConstant.TRADE_STATUS_FAILED.equals(depositStatus)) {
			processTradeStatusFailed(tppTradeLog, tpp, notifyEvent);
		}

		logger.info(MessageFormat.format("++++ {0}订单号{1},状态{2},异步回调成功!", loggerPrefix, outTradeNo, depositStatus));
	}

	/**
	 * 代收/代付交易成功后是否需要处理业务逻辑
	 * @return
	 */
	public abstract boolean supprotTradeFinished(String serviceType);

	/**
	 * 代收/代付交易完成状态处理，子类实现是否需要处理。(对于投标/自动投标不需要处理)
	 * @param tppTradeLog
	 * @param tpp
	 * @param event
	 * @throws TaskProcessorException
	 */
	private void dosTradeFinished(TppTradeLog tppTradeLog, Tpp tpp, CollectionTradeNotifyValueEvent event) throws TaskProcessorException{

		if(!supprotTradeFinished(tppTradeLog.getServiceType())){
			return;
		}

		//获取代收/代付类型，用于日志输出
		String loggerPrefix = getLoggerPrefix(tppTradeLog.getTradeType());

		String outTradeNo = event.getOutOrderNo();
		if (tpp == null) {
			logger.info(MessageFormat.format("++++ 等待生成{0}订单号{1}关联的支付信息Tpp!", loggerPrefix, outTradeNo));
			throw new TaskProcessorException(event);
		}

		if ((tpp.getStatus() != TppModel.STATUS_SUCEESS)) {
			tpp.setStatus(TppModel.STATUS_SUCEESS);
			tpp.setRespDesc("代收成功");
			tppService.update(tpp);
			logger.info(MessageFormat.format("++++ 代收订单号{0}状态更新为成功!", outTradeNo));
		}
	}

	/**
	 * 代收/代付回调支付成功后，保存数据处理
	 * 如果是代收，需要继续处理doPay()逻辑发起代付请求
	 * @param tppTradeLog
	 * @param event
	 * @throws TaskProcessorException
	 */
	private void doPayFinished(TppTradeLog tppTradeLog, CollectionTradeNotifyValueEvent event) throws TaskProcessorException{
		tppTradeLog.setAmount(event.getDepositAmount());
		tppTradeLog.setTradeStatus(event.getDepositStatus());
		tppTradeLog.setTradeTime(DateUtil.getNow());
		tppTradeService.update(tppTradeLog);
		TradeCode tradeCode = tradeCode();
		if(tradeCode != null && tradeCode.toString().equals(tppTradeLog.getTradeType())){
			doPay(tppTradeLog);
		}
	}

	/**
	 * 代收的请求交易码，只能设置TradeCode.C1001和TradeC1002
	 * @return
	 */
	public abstract TradeCode tradeCode();

	/**
	 * 代收成功后处理逻辑，子类中实现发起代付请求
	 */
	protected abstract void doPay(TppTradeLog tppTradeLog);

	private void processTradeStatusFailed(TppTradeLog tppTradeLog, Tpp tpp, CollectionTradeNotifyValueEvent event) throws TaskProcessorException{
		tppTradeLog.setTradeStatus(event.getDepositStatus());
		tppTradeLog.setTradeTime(DateUtil.getNow());
		doFail(tppTradeLog, tpp, event);
		tppTradeService.update(tppTradeLog);
	}

	/**
	 * 失败状态处理逻辑
	 * @param tppTradeLog
	 * @param tpp
	 * @param event
	 * @throws TaskProcessorException
	 */
	protected abstract void doFail(TppTradeLog tppTradeLog, Tpp tpp, CollectionTradeNotifyValueEvent event) throws TaskProcessorException;

	/**
	 * 获取代收/代付的日志前缀信息
	 * @param tradeType
	 * @return
	 */
	protected String getLoggerPrefix(String tradeType){
		TradeCode tradeCode = TradeCode.getTradeCodeByType(tradeType);
		if(TradeCode.C1001.equals(tradeCode) || TradeCode.C1002.equals(tradeCode)){
			return "[代收]";
		}else if(TradeCode.C2001.equals(tradeCode) || TradeCode.C2002.equals(tradeCode)){
			return "[代付]";
		}else{
			return "[无效代收/代付]";
		}
	}

	/**
	 * 重写异常处理：延迟1s，然后重放到队列中执行。解决异步通知时，数据还没有保存到数据库中的问题
	 * @param hander
	 * @param event
	 * @param exception
	 */
	@SuppressWarnings("static-access")
	@Override
	public void exceptionProcess(ValueEvent event, Exception exception) {
		try {
			Thread.currentThread().sleep(1000);
			super.exceptionProcess(event, exception);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}


}
